package com.yael.utils;

import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.util.logging.Logger;

import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileReadChannel;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.FileWriteChannel;
import com.google.appengine.api.files.GSFileOptions.GSFileOptionsBuilder;
import com.google.appengine.api.files.LockException;

public class GSManager {
	
	private static final Logger logger = Logger.getLogger(GSManager.class.getName());
	private static final String DEFAULT_ACL = "public_read"; 			//Default permissions for uploaded file
	private static final String DEFAULT_CACHE = "public, max-age=60"; 	//Default cacheControl: 1 min
	private static final String DEFAULT_ENCODING = "UTF8";
	private FileService fileService = null;
	
	public GSManager(){
		this.fileService = FileServiceFactory.getFileService();
	}
	
	public FileWriteChannel createWritableFile(String bucketName, String fileName) throws IOException {
		return createWritableFile( bucketName, fileName,  getMimeType(fileName), DEFAULT_ACL, DEFAULT_CACHE);
	}
	
	public FileWriteChannel createWritableFile(String bucketName, String fileName, String mimeType, String permission, String cacheControl) throws IOException {
		GSFileOptionsBuilder optionsBuilder = null;
		AppEngineFile writableFile = null;
		String path = null;
		
		if(Utils.isEmpty(bucketName)||Utils.isEmpty(fileName)){
			logger.warning("bucketName : " + bucketName + " fileName : " + fileName);
			throw new IOException("Invalid Bucket or File name!"); 
		}
		
		optionsBuilder = new GSFileOptionsBuilder();
		optionsBuilder.setBucket(bucketName);
		optionsBuilder.setKey(fileName);
		optionsBuilder.setMimeType(mimeType);
		optionsBuilder.setAcl(permission);
		optionsBuilder.setCacheControl(cacheControl);
			
		writableFile = this.fileService.createNewGSFile(optionsBuilder.build());
		path = writableFile.getFullPath();
		writableFile = new AppEngineFile(path);
	  //logger.info(path+" isWritable:"+writableFile.isWritable());
		return fileService.openWriteChannel(writableFile, true);
	}
	
	public void closeWritableFile(FileWriteChannel file){
		try {
			if(file != null && file.isOpen()) file.closeFinally();
		} catch (IllegalStateException e) {
			logger.warning("IllegalStateException" + e);
			e.printStackTrace();
		} catch (IOException e) {
			logger.warning("IOException" + e);
			e.printStackTrace();
		}
	}
	
	public void setObject(String bucketName, String fileName, byte[] data) throws IOException {
		setObject(bucketName, fileName, getMimeType(fileName), DEFAULT_ACL, DEFAULT_CACHE, data, DEFAULT_ENCODING);
	}
	
	public void setObject(String bucketName, String fileName, String mimeType, String permission, String cacheControl, byte[] data, String encoding) throws IOException {
		FileWriteChannel writeChannel = null;
		
		try{
			writeChannel = createWritableFile(bucketName,fileName,mimeType,permission,cacheControl);
			writeChannel.write(ByteBuffer.wrap(data), encoding);
		} catch (IOException e) {
			logger.warning("IOException: " + e);
			e.printStackTrace();
			throw e;
		} finally {
			try {
				if(writeChannel != null && writeChannel.isOpen()) writeChannel.closeFinally();
			} catch (IllegalStateException e) {
				logger.warning("IllegalStateException: " + e);
			} catch (IOException e) {
				logger.warning("IOException: " + e);
			}
		}
	}
	
	public byte[] getObject(String bucketName, String filename)
	{	
		byte[] data = new byte [16384]; //switch to dynamic array
		boolean lockForRead = false;
		AppEngineFile readableFile = new AppEngineFile("/gs/"+bucketName+filename);
		
		FileReadChannel readChannel;
		try {
			readChannel = fileService.openReadChannel(readableFile, lockForRead);
			DataInputStream reader = new DataInputStream(Channels.newInputStream(readChannel));
			reader.readFully(data);
			readChannel.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (LockException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return data;
	}
	
//	public Map<String, String> getObjectList(String bucketName)
//	{	
//		Document doc = XmlUtil.parse("http://storage.googleapis.com/"+bucketName);
//		NodeList nodes = doc.getElementsByTagName("Key");
//		//NodeList nodes = XmlUtil.selectNodes(doc, "//http://doc.s3.amazonaws.com/2006-03-01:Contents/http://doc.s3.amazonaws.com/2006-03-01:Key/text()");
//		
//		Map<String, String> images = new HashMap<String, String>(nodes.getLength());
//		for(int i=0; i<nodes.getLength();i++){
//			images.put(nodes.item(i).getTextContent(), "");
//		}
//		return images;
//	}
	
	public static String getMimeType(String fileName){
		String type = findMimeType(fileName);
		return (type==null)? "text/html": type;
	}
	
	public static boolean hasMimeType(String fileName){
		String type = findMimeType(fileName);
		return (type==null)? false: true;
	}
	
	private static String findMimeType(String fileName){
		String type = fileName.substring(fileName.lastIndexOf(".") + 1);
		if(type.equalsIgnoreCase("xml")) return "text/xml";
		if(type.equalsIgnoreCase("jpg")) return "image/jpeg";
		if(type.equalsIgnoreCase("jpeg")) return "image/jpeg";
		if(type.equalsIgnoreCase("jpe")) return "image/jpeg";
		if(type.equalsIgnoreCase("gif")) return "image/gif";
		if(type.equalsIgnoreCase("png")) return "image/png";
		if(type.equalsIgnoreCase("htm")) return "text/html";
		if(type.equalsIgnoreCase("html")) return "text/html";
		if(type.equalsIgnoreCase("htm")) return "text/html";
		if(type.equalsIgnoreCase("txt")) return "text/plain";
		else return null;
	}
	
	
}
